package com.aepryus.loom;

import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.aepryus.util.*;
import com.aepryus.xml.*;
import com.aepryus.rpc.*;

public abstract class Controller extends HttpServlet {
	public static Controller controller;
	private Tracker tracker = new Tracker();
	private boolean encryptYN = false;
	private String name;
	private String location;
	private Settings settings;

	private Register<Action> actionRegister = new Register<Action>("action");
	private Register<Persist> persistRegister = new Register<Persist>("persist");
	private Register<View> viewRegister = new Register<View>("view");
	private Register<StringTo> stringToRegister = new Register<StringTo>("stringTo");

	static {
	    XML.addXMLizer("method",new MethodXMLizer());
	    XML.addXMLizer("parameter",new ParameterXMLizer());
	    XML.addXMLizer("String",new StringXMLizer());
	    XML.addXMLizer("long",new LongXMLizer());
	}

	public Controller () {
		controller = this;
	}

	protected abstract String getController();
	protected abstract String getLoginView();
	protected abstract String getLoginAction();
	protected abstract String getHomeURL();
	protected abstract String getAboutURL();
	protected abstract void initializePersistence() throws PersistenceException;

	public Tracker getTracker () {return tracker;}
	protected void setEncryptYN (boolean encryptYN) {this.encryptYN = encryptYN;}
	public boolean getEncryptYN () {return encryptYN;}
	public void setName (String name) {this.name = name;}
	public String getName () {return name;}
	public void setLocation (String location) {this.location = location;}
	public String getLocation () {return location;}
	public void setSettings (Settings settings) {this.settings = settings;}
	public Settings getSettings () {return settings;}

	public void addAction (Action action) {actionRegister.register(action);}
	public Action getAction (String name) {return actionRegister.getEntry(name);}
	public void addPersist (Persist persist) {persistRegister.register(persist);}
	public Persist getPersist (String name) {return persistRegister.getEntry(name);}
	public Set<String> getPersistKeySet () {return persistRegister.keySet();}
	public void addView (View view) {viewRegister.register(view);}
	public void addStringTo (StringTo stringTo) {stringToRegister.register(stringTo);}
	public StringTo getStringTo (String name) {return stringToRegister.getEntry(name);}

// HttpServlet =========================================================================================================
	public void init (ServletConfig config) throws ServletException {
		super.init(config);

		File file = new File(getLocation());
		if (!file.exists())
			Util.createDirectory(getLocation());
		file = new File(getLocation()+"/uploads");
		if (!file.exists())
			Util.createDirectory(getLocation()+"/uploads");

		FromString.initialize(this);
		XML.addXMLizer("settings",new SettingsXMLizer());
      	try {
			FileInputStream fis = new FileInputStream(getLocation()+"/"+getName()+".xml");
			settings = (Settings)XML.toObject(Util.streamToString(fis));
		} catch (Exception e) {
			settings = new Settings();
            settings.setRDBMS("oracle");
            settings.setDBClass("oracle.jdbc.driver.OracleDriver");
			settings.setDBURL("jdbc:oracle:thin:@localhost:1521:peach");
			settings.setUsername(getName());
			settings.setPassword(getName());
			settings.setRequireLoginYN(false);
			settings.setPasswordEncryptionKey("Aepryus Software");
			try {
				Util.StringToFile(getLocation()+"/"+getName()+".xml",XML.toXML(settings));
			} catch (IOException e2) {
				throw new RuntimeException(e2);
			}
		}
	}
	protected void doGet (HttpServletRequest request,HttpServletResponse response) throws IOException {
		route(request,response);
	}
	protected void doPost (HttpServletRequest request,HttpServletResponse response) throws IOException {
		if (request.getContentType().startsWith("multipart/form-data"))
			routeMultipart(request,response);
		else if (request.getParameter("view") == null) {
			routeAPI(request,response);
}		else
			route(request,response);
	}
	public String getServletInfo () {
		return "Aepryus Loom Controller";
	}

// =====================================================================================================================
	protected void route (HttpServletRequest request,HttpServletResponse response) throws IOException {
		try {
			if (getEncryptYN() && !"https".equals(request.getScheme())) {
				response.sendRedirect("https://"+request.getServerName()+request.getRequestURI());
				return;
			}

			tracker.trackSession(request.getSession());

		    HttpSession session = request.getSession();
		    Credentials credentials = (Credentials)session.getAttribute("credentials");
		    if (credentials == null) {
				if (settings.getRequireLoginYN()) {
					if (request.getQueryString() != null) {
						response.sendRedirect("");
						return;
					}
					if (!"login".equals(request.getParameter("action"))) {
						View view = viewRegister.getEntry(getLoginView());
						Log.print("view:"+view.getName());
						RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(view.getJSP(request));
						dispatcher.forward(request,response);
					} else {
						Action action = actionRegister.getEntry(getLoginAction());
						Log.print("action:"+action.getName());
						action.doProcess(request);

			  			RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(getHomeURL());
						dispatcher.forward(request,response);
					}
					return;
				} else {
					credentials = new Credentials();
					credentials.setUsernameID(0);
					credentials.setUsername("xxx");
					credentials.setName("anonymous");
				    credentials.setMenuID(1);
					request.getSession().setAttribute("credentials",credentials);
					request.getSession().setMaxInactiveInterval(60*60);
					Log.print(" ( xxx ) - login ");
				}
		    }

		    Trace trace = (Trace)request.getSession().getAttribute("trace");
		    if (trace == null) {
				trace = new Trace(this);
				request.getSession().setAttribute("trace",trace);
		    }

			if (request.getParameter("back") != null) {
				response.sendRedirect(trace.back(request));
				return;
			} else if (request.getParameter("fore") != null) {
				response.sendRedirect(trace.fore(request));
				return;
		    }

		    if (request.getParameter("view") == null) {
	  			RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(getHomeURL());
				dispatcher.forward(request,response);
				return;
		    }

			if (request.getParameter("action") != null) {
				try {
					Action action = actionRegister.getEntry(request.getParameter("action"));
					Log.print("action:"+action.getName());
					Object result = action.doProcess(request);
					if (result != null)
						request.setAttribute("result",result);
					if ("logout".equals(request.getParameter("action"))) {
						response.sendRedirect("");
						return;
					}
				} catch (ValidationException ve) {
/*					Map parameters = new HashMap();
					for (Enumeration e=request.getParameterNames();e.hasMoreElements();) {
						String name = (String)e.nextElement();
						parameters.put(name,request.getParameter(name));
					}
					ve.setParameters(parameters);*/
Log.print(">}=== VALIDATION ==={<");
Log.print(ve);
					request.setAttribute("validation",ve);
				}
			}

			View view = viewRegister.getEntry(request.getParameter("view"));
			Log.print("view:"+view.getName());
		    credentials.setLastView(view.getName());
//			Bundle screen = view.process(request);
//		    if (screen != null)
//			    request.setAttribute("screen",screen);

			if (view.traceable())
				trace.trace(name,view.getName(),view.getQuery());
			else
				trace.sneak();

			RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(view.getJSP(request));
			dispatcher.forward(request,response);

		} catch (ServletException e) {
		    Log.print(e);
		    StringBuffer sb = new StringBuffer();

		    sb.append(Util.traceToString(e));
			if (e.getRootCause() != null) {
				sb.append("\r\n<B>ROOT CAUSE:</B>\r\n\r\n");
				sb.append(Util.traceToString(e.getRootCause()));
			}

			request.setAttribute("exception",sb.toString());
		    error(request,response);
		} catch (Throwable t) {
		    Log.print(t);
		    request.setAttribute("exception",Util.traceToString(t));
		    error(request,response);
		}
	}
	private void error (HttpServletRequest request,HttpServletResponse response) {
		try {
			RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/Error.jsp");
			dispatcher.forward(request,response);
		} catch (Exception e) {
		    Log.print("Error with Error Handler");
		    Log.print(e);
		}
	}
	public void routeAPI (HttpServletRequest request,HttpServletResponse response) {
		try {

/*			tracker.trackSession(request.getSession());
		    HttpSession session = request.getSession();
		    Credentials credentials = (Credentials)session.getValue("credentials");
		    if (credentials == null) {
				credentials = new Credentials();
				credentials.setUsername("api");
				credentials.setName("api");
				request.getSession().putValue("credentials",credentials);
				request.getSession().setMaxInactiveInterval(24*60*60);
				Log.print(" ( "+credentials.getUsername()+" ) - login ");
		    }*/

		    String xml = Util.streamToString(request.getInputStream());
			Method method = (Method)XML.toObject(xml);
			Action action = actionRegister.getEntry(method.getName());

		    Map<String,String> parameters = new HashMap<String,String>();
		    for (Iterator i=method.getParameterIterator();i.hasNext();) {
				String name = (String)i.next();
				Parameter parameter = method.getParameter(name);
				parameters.put(name,(String)parameter.getValue());
		    }

			Object result = action.doProcess(parameters,request);
		    if (result != null)
				response.getWriter().print(XML.toXML(result));

		} catch (Throwable t) {
		    try {
			    t.printStackTrace(response.getWriter());
		    } catch (IOException e) {
				Log.print(e);
			}
		}
	}
	private void routeMultipart (HttpServletRequest request,HttpServletResponse response) throws IOException {
		HttpServletRequest picture = new MultiRequest(request,getLocation()+"/uploads");
		route(picture,response);
	}
}

